package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.es.SkuHasStock;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberResp;
import com.atguigu.gulimall.order.InterCeptor.LoginInterCeptor;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.atguigu.gulimall.order.feigin.CartFeignService;
import com.atguigu.gulimall.order.feigin.MemberFeiginService;
import com.atguigu.gulimall.order.feigin.ProductFeignService;
import com.atguigu.gulimall.order.feigin.WareFeignService;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    // 一个线程可以同时拥有多个  存储不同类型数据的 ThreadLocal
    private static ThreadLocal<OrderSubmitVo> oSVThreadLocal=new ThreadLocal<>();
    @Autowired
    OrderItemService orderItemService;

    @Autowired
    MemberFeiginService memberFeiginService;

    @Autowired
    CartFeignService cartFeignService;


    @Autowired
    ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    WareFeignService wareFeignService;


    @Autowired
    StringRedisTemplate stringRedisTemplate;


    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;





    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }


    /**
     * 订单确认页 数据返回
     * @return
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberResp memberResp = LoginInterCeptor.threadLocal.get();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 获取会员的所有的 收获地址信息
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> receiveAddress = memberFeiginService.getAllReceiveAddress(memberResp.getId());
            orderConfirmVo.setMemberAddressVos(receiveAddress);
        }, threadPoolExecutor);

        // 获取所有购物车项
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> itemVoList = cartFeignService.getItemsToOrderConfirm();
            orderConfirmVo.setItems(itemVoList);
        }, threadPoolExecutor).thenRunAsync(()->{
            //库存批量查询
            List<OrderItemVo> items = orderConfirmVo.getItems();
            // 所有商品的skuId
            List<Long> collect = items.stream().map((item) -> item.getSkuId()).collect(Collectors.toList());
            List<SkuHasStock> skuHasStock = wareFeignService.getSkuHasStock(collect);
            Map<Long, Boolean> booleanMap = skuHasStock.stream().collect(Collectors.toMap(SkuHasStock::getSkuId, SkuHasStock::isHasStock));
            orderConfirmVo.setStocks(booleanMap);
        });

        CompletableFuture.allOf(future,future1).get();

        List<OrderItemVo> itemVoList = cartFeignService.getItemsToOrderConfirm();
         orderConfirmVo.setItems(itemVoList);

         orderConfirmVo.setIntegration(memberResp.getIntegration());

          // 当购物车页面 跳转到 订单页  生成 防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        stringRedisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN_PREFIX+memberResp.getId(),token);

        orderConfirmVo.setOrderToken(token); // 目的 在创建订单时 用于表单提交 与redis中的数据校验 防止接口幂等性

        return orderConfirmVo;
    }


    /**
     *  用于 创建订单前  去删除令牌  防止 接口幂等性
     * @param orderSubmitVo  用户传过来的对象
     * @return
     */
    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        // 方便后续的 获取值
        oSVThreadLocal.set(orderSubmitVo);

        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        MemberResp memberResp = LoginInterCeptor.threadLocal.get();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";


        Long execute = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class)
                , Arrays.asList(OrderConstant.ORDER_TOKEN_PREFIX + memberResp.getId()), orderSubmitVo.getOrderToken());
          // 删除失败
        if(execute==0l){
               responseVo.setCode(1);
              return responseVo;
          }else{
            // 删除成功  创建订单 锁库存
            OrderCreateTo orderCreateTo = createOrder();
            //校验价格
            if(orderCreateTo.getPayTrueMoney().subtract(orderSubmitVo.getPayPrice()).doubleValue()<0.01){
                // 接下来的 操作  数据库保存数据  锁库存 这个操作也涉及到远程调用 所以返回  R对象
                R respon=saveOrderAndLockStock(orderCreateTo);

               if(respon.getCode()==0){
                   responseVo.setOrder(orderCreateTo.getOrderentity());
                   // 把控 发送消息的时机  最好在 订单创建成功  库存锁定成功  再发
                   rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",orderCreateTo.getOrderentity());
                   return  responseVo;
               }else{
                   responseVo.setCode(3);
                   return  responseVo;
               }

            }else{
                //比价 失败
                responseVo.setCode(2);
                return  responseVo;
            }

        }
    }

    /**
     *  通过 订单id  获取 顶 单状态
     * @param orderSn
     * @return
     */
    @Override
    public OrderEntity getOrderStatusByOrderSn(String orderSn) {
        OrderEntity order_sn = getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return order_sn;
    }

    /**
     *  mq监听 处理 过期的 订单
     * @param orderEntity
     */
    @Override
    public void handleReleaseOrder(OrderEntity orderEntity) {
        // 先从数据库中判断 传过来的订单数据 是否在对应的 数据库表中有数据   1 有 就行处理  2 没有说明 订单发生了回滚
        OrderEntity order_sn = getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderEntity.getOrderSn()));

        // 这里注意 Integer 的数值在 -128~127 用的引用会是同一个  所以 要用 equals
        if(order_sn!=null && OrderStatusEnum.CREATE_NEW.getCode().equals(order_sn.getStatus())){
           update(new UpdateWrapper<OrderEntity>()
                   .set("status",OrderStatusEnum.CANCLED.getCode())
                   .eq("order_sn",OrderStatusEnum.CANCLED.getCode()));
         // 此时向库存服务发送 被监听的队列 发送监听的信息    防止订单网络超时   库存服务提前去消费  造成无法解锁对应商品库存的消息
            rabbitTemplate.convertAndSend("order-event-exchange","order.release.order.#",order_sn.getOrderSn());
        }
    }

    @Override
    public PayVo getPayVoByOrdersn(String orderSn) {
        PayVo payVo = new PayVo();
        OrderEntity orderInfo = getOne(new QueryWrapper<OrderEntity>().eq("order_sn",orderSn));

        //保留两位小数点，向上取值
        BigDecimal payAmount = orderInfo.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
        payVo.setTotal_amount(payAmount.toString());
        payVo.setOut_trade_no(orderInfo.getOrderSn());

        //查询订单项的数据
        List<OrderItemEntity> orderItemInfo = orderItemService.list(
                new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        OrderItemEntity orderItemEntity = orderItemInfo.get(0);
        payVo.setBody(orderItemEntity.getSkuAttrsVals());

        payVo.setSubject(orderItemEntity.getSkuName());

        return payVo;
    }

    /**
     * 根据用户的 Id查询 对应的所有订单  以及所有的订单 关联订单项
     * @param params
     * @return
     */
    @Override
    public PageUtils listWithItem(Map<String, Object> params) {
        MemberResp memberResp = LoginInterCeptor.threadLocal.get();


        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
                        .eq("member_id",memberResp.getId()).orderByDesc("create_time")
        );

        //遍历所有订单集合
        List<OrderEntity> orderEntityList = page.getRecords().stream().map(order -> {
            //根据订单号查询订单项里的数据
            List<OrderItemEntity> orderItemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>()
                    .eq("order_sn", order.getOrderSn()));
            order.setOrderItemEntityList(orderItemEntities);
            return order;
        }).collect(Collectors.toList());

        page.setRecords(orderEntityList);

        return new PageUtils(page);
    }


    private R saveOrderAndLockStock(OrderCreateTo order) {
        // 保存订单表
        save(order.getOrderentity());
        // 批量保存 订单相关联 的 购物项
        orderItemService.saveBatch(order.getOrderItems());

         // 远程调用库存服务 锁库存
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(order.getOrderentity().getOrderSn());

        List<OrderItemVo> orderItemVoList = order.getOrderItems().stream().map((entity) -> {
            OrderItemVo orderItemVo = new OrderItemVo();
            orderItemVo.setSkuId(entity.getSkuId());
            orderItemVo.setCount(entity.getSkuQuantity());
            return orderItemVo;
        }).collect(Collectors.toList());
            wareSkuLockVo.setLocks(orderItemVoList);

        // 锁定库存
        R r = wareFeignService.saveOrderToLockStock(wareSkuLockVo);

        return r;
    }


    // 创建订单
    private OrderCreateTo createOrder(){
        OrderCreateTo orderCreateTo = new OrderCreateTo();

        // 创建随机 订单号
        String order_Sn = IdWorker.getTimeId();
        // 构建 订单详情表数据  orderEntity
       OrderEntity orderEntity= bulidOrderEntity(oSVThreadLocal.get(),order_Sn);
       orderCreateTo.setOrderentity(orderEntity);
       // 构建 订单详情关联的所有的 购物项 需要插入到对应的表中  List<OrderItemEntity>
        List<OrderItemEntity> orderItemEntities=bulidOrderItemEntites(order_Sn);
        orderCreateTo.setOrderItems(orderItemEntities);

        // 比对 订单详情页  与购物车的所有项的 价格   比对价格 以及设置订单总价  以及 总优惠信息
        checkPrice(orderEntity,orderItemEntities);

        orderCreateTo.setPayTrueMoney(orderEntity.getPayAmount());
        orderCreateTo.setFare(orderEntity.getFreightAmount());
        return orderCreateTo;
    }


    /**
     *
     * @param orderEntity  总订单  的 总价格 总优惠相关 的信息需要从 关联的订单项中一 一获取
     * @param orderItemEntities
     */
    private void checkPrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {

        //总信息  注意每一个总信息都需要从 所有关联的订单项中获取
        BigDecimal totalAmount = new BigDecimal(0);
        BigDecimal couponTotal = new BigDecimal(0);
        BigDecimal promoteTotal = new BigDecimal(0);
        BigDecimal integrationTotal = new BigDecimal(0);

        for (OrderItemEntity orderItemEntity:orderItemEntities) {
              totalAmount= totalAmount.add(orderItemEntity.getRealAmount());
              couponTotal=couponTotal.add(orderItemEntity.getCouponAmount());
              promoteTotal=promoteTotal.add(orderItemEntity.getPromotionAmount());
              integrationTotal=integrationTotal.add(orderItemEntity.getIntegrationAmount());
        }

        orderEntity.setTotalAmount(totalAmount);
        // 应付总额等于 总价 加运费
        orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount()));
        orderEntity.setCouponAmount(couponTotal);
        orderEntity.setPromotionAmount(promoteTotal);
        orderEntity.setIntegrationAmount(integrationTotal);

    }


    private List<OrderItemEntity> bulidOrderItemEntites(String order_sn) {
        List<OrderItemVo> currentItems = cartFeignService.getItemsToOrderConfirm();
        if(currentItems!=null&&currentItems.size()!=0){
            List<OrderItemEntity> orderItemEntities = currentItems.stream().map((orderItemVo) -> {
                OrderItemEntity orderItemEntity = bulidOrderItemEntity(orderItemVo,order_sn);
                return orderItemEntity;
            }).collect(Collectors.toList());
            return  orderItemEntities;
        }
        return null;
    }

    // 构建从 购物车取过来的的vo 转换为 数据表要 插入的数据格式
    private OrderItemEntity bulidOrderItemEntity(OrderItemVo orderItemVo, String order_sn) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(order_sn);
        Long skuId = orderItemVo.getSkuId();

         // 1 设置 关于 spu的信息
        //获取spu的信息
        R spuInfo = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfoData = spuInfo.getData("data", new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(spuInfoData.getId());
        orderItemEntity.setSpuName(spuInfoData.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoData.getBrandId().toString());
        orderItemEntity.setCategoryId(spuInfoData.getCatalogId());



        // 设置表中与 sku相关的信息
        //2、商品的sku信息
        orderItemEntity.setSkuId(skuId);
        orderItemEntity.setSkuName(orderItemVo.getTitle());
        orderItemEntity.setSkuPic(orderItemVo.getImage());
        orderItemEntity.setSkuPrice(orderItemVo.getPrice());
        orderItemEntity.setSkuQuantity(orderItemVo.getCount());

        //使用StringUtils.collectionToDelimitedString将list集合转换为String
        String skuAttrValues = StringUtils.collectionToDelimitedString(orderItemVo.getSkuAttrValues(), ";");
        orderItemEntity.setSkuAttrsVals(skuAttrValues);

        //3、商品的优惠信息  这里获取优惠信息 会涉及到远程调用   所以直接设置值
        orderItemEntity.setCouponAmount(new BigDecimal(0));
        orderItemEntity.setPromotionAmount(new BigDecimal(0));
        orderItemEntity.setIntegrationAmount(new BigDecimal(0));

         //设置应付金额
          BigDecimal realPrice=orderItemEntity.getRealAmountByVo(orderItemVo);
           orderItemEntity.setRealAmount(realPrice);

        //4、商品的积分信息
        orderItemEntity.setGiftGrowth(orderItemVo.getPrice().multiply(new BigDecimal(orderItemVo.getCount())).intValue());
        orderItemEntity.setGiftIntegration(orderItemVo.getPrice().multiply(new BigDecimal(orderItemVo.getCount())).intValue());


        return orderItemEntity;
    }


    // 构建 创建订单时 在订单详情表中插入数据
    private OrderEntity bulidOrderEntity(OrderSubmitVo orderSubmitVo,String orderSn) {
        OrderEntity orderEntity = new OrderEntity();

         orderEntity.setOrderSn(orderSn);
         //设置用户id   member_Id
        orderEntity.setMemberId(LoginInterCeptor.threadLocal.get().getId());


         //远程调用库存服务 获取 收获地址的详情信息 以及运费
        R fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        FareVo fareVo = fare.getData(new TypeReference<FareVo>() {});
        orderEntity.setFreightAmount(fareVo.getFare());



        //获取到收货地址信息
        MemberAddressVo address = fareVo.getAddress();
        //设置收货人信息
        orderEntity.setReceiverName(address.getName());
        orderEntity.setReceiverPhone(address.getPhone());
        orderEntity.setReceiverPostCode(address.getPostCode());
        orderEntity.setReceiverProvince(address.getProvince());
        orderEntity.setReceiverCity(address.getCity());
        orderEntity.setReceiverRegion(address.getRegion());
        orderEntity.setReceiverDetailAddress(address.getDetailAddress());
       //设置订单状态
        orderEntity.setAutoConfirmDay(7);
        orderEntity.setConfirmStatus(OrderStatusEnum.CREATE_NEW.getCode());

        return orderEntity;
    }


}